package leyou.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.leyou.common.to.Goods;
import leyou.search.constant.Esconstant;
import leyou.search.service.EsGoodsSearchService;
import leyou.search.vo.SearchPamarVo;
import leyou.search.vo.SerachResults;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {


    @Resource(name = "restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 商品查询
     * @param pamarVo
     * @return
     */
    @Override
    public SerachResults search(SearchPamarVo pamarVo) {

        //获取查询请求  参数：  查询的索引的名(类似数据库的名字)
        SearchRequest request =sourceBuilderRequest(pamarVo);
        // 构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        SerachResults result = null;

        try {
            //  执行检索
            SearchResponse searchResponse = client.search(request,RequestOptions.DEFAULT);

            result = SearchSourceBuilderResult(searchResponse,pamarVo);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 封装结果集
     * @return
     */
    private SerachResults SearchSourceBuilderResult(SearchResponse response,SearchPamarVo param) {
        SerachResults result = new SerachResults();
        // 1.   封装符合条件的商品数据
        List<Goods> goods  = new ArrayList<>();
        SearchHits hits = response.getHits();
        if(hits.getHits() != null && hits.getHits().length>0){
            for (SearchHit hit : hits.getHits()) {
                String sourceAsString = hit.getSourceAsString();
                //  将json字符串装换成实体类对象
                Goods model = JSON.parseObject(sourceAsString,Goods.class);
                if(!StringUtils.isEmpty(param.getKey())){
                    //  获取高亮字段的信息
                    HighlightField title = hit.getHighlightFields().get("subTitle");
                    if(title != null){
                        model.setSubTitle(title.getFragments()[0].toString());
                    }
                }
                goods.add(model);
            }
        }
        result.setGoodsList(goods);

        // 2.  当前商品涉及到的 品牌
        //  通过聚合的名字 获取当前聚合的对象
        ParsedLongTerms brandAagg = response.getAggregations().get("brand_agg");
        List<SerachResults.brandVo> brandVos = new ArrayList<>();
        List<? extends Terms.Bucket> buckets = brandAagg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            SerachResults.brandVo brandVo = new SerachResults.brandVo();
            // 品牌id
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
            //2、得到品牌的名
            String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
            //3、得到品牌的图片
            String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);
            brandVo.setBrandimg(brandImg);
            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);


        // 3.  当前商品涉及到的所有的分类信息

        ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
        List<SerachResults.catalogVo> catalogVos = new ArrayList<>();
        List<? extends Terms.Bucket> catalogbuckets = catalog_agg.getBuckets();
        for (Terms.Bucket bucket : catalogbuckets) {
            SerachResults.catalogVo catalogVo = new SerachResults.catalogVo();
            //得到分类id
            String keyAsString = bucket.getKeyAsString();
            catalogVo.setCatId(Long.parseLong(keyAsString));
            //得到分类名
            ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
            String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatName(catalog_name);
            catalogVos.add(catalogVo);
        }
        result.setCatalogs(catalogVos);

        // 4 .当前商品涉及到的所有属性信息

        List<SerachResults.specs> specs = new ArrayList<>();
        ParsedNested attr_agg = response.getAggregations().get("specs_agg");
        ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("specsIdAgg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SerachResults.specs attrVo = new SerachResults.specs();
            //1、得到属性的id;
            long attrId = bucket.getKeyAsNumber().longValue();
            //2、得到属性的名字
            String attrName = ((ParsedStringTerms) bucket.getAggregations().get("specsNameAgg")).getBuckets().get(0).getKeyAsString();
            //3、得到属性的所有值
            List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("specsValueAgg")).getBuckets().stream().map(item -> {
                String keyAsString = item.getKeyAsString();
                return keyAsString;

            }).collect(Collectors.toList());
            attrVo.setSpecId(attrId);
            attrVo.setSpecName(attrName);
            attrVo.setSpecValue(attrValues);
            specs.add(attrVo);
        }
        result.setSpecsList(specs);

        // 5. 分页信息
        result.setPageNum(param.getPage());
        result.setTotal(hits.getTotalHits().value);    //  总记录数
        Long num = 0L;
        if(param.getPageSize() != null){
             num = hits.getTotalHits().value%param.getPageSize()==0?hits.getTotalHits().value/param.getPageSize():(hits.getTotalHits().value/param.getPageSize())+1;

        }else {
             num = hits.getTotalHits().value%Esconstant.DEFAULT_SIZE==0?hits.getTotalHits().value/Esconstant.DEFAULT_SIZE:(hits.getTotalHits().value/Esconstant.DEFAULT_SIZE)+1;
        }
        result.setTotalPages(num);  // 总页数

        return result;
    }

    /**
     * 构建查询条件
     * 模糊配置 过滤（按照属性，分类，品牌，价格区间，库存）， 排序 ，分页 ，高亮, 聚合
     * @param pamarVo
     * @return
     */
    private SearchRequest sourceBuilderRequest(SearchPamarVo pamarVo) {

        // 准备dsl语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /**
         * 过滤（按照属性，，品牌，价格区间，库存）
         */
        //  1 构建bool
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.1 must-模糊匹配
        if(!StringUtils.isEmpty(pamarVo.getKey())){
            boolQuery.must(QueryBuilders.matchQuery("all",pamarVo.getKey()));
        }

        // 1.2  过滤  filter
        if(pamarVo.getCatalogId() != null){
            //  1.2.1 按照三级分类id类查询
            boolQuery.filter(QueryBuilders.termQuery("cid3",pamarVo.getCatalogId()));
        }
        if(pamarVo.getBrandId() != null ){
            // 1.2.2  按照品牌id来查询  brandId
            boolQuery.filter(QueryBuilders.termQuery("brandId",pamarVo.getBrandId()));
        }

        /**
         *  按属性 id,名称查询
         */
        if(pamarVo.getFilter() != null && pamarVo.getFilter().size() >0 ){
            pamarVo.getFilter().forEach( (k,v)->{
                BoolQueryBuilder nestedBuilder = QueryBuilders.boolQuery();
                nestedBuilder.must(QueryBuilders.termQuery("specs.specId",k));
                if(!StringUtils.isEmpty(v)){
                    nestedBuilder.must(QueryBuilders.termQuery("specs.specValue",v));
                }
                NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("specs",nestedBuilder,ScoreMode.None);
                boolQuery.filter(nestedQueryBuilder);
            });
        }
        // 构建bool
        sourceBuilder.query(boolQuery);

        /**
         *  排序 ，分页 ，高亮,
         */
        //  sort
        //排序：sort=price/salecount/hotscore_desc/asc
        if(!StringUtils.isEmpty(pamarVo.getSortbY())){
            SortOrder order =  pamarVo.getDescending()?SortOrder.DESC:SortOrder.ASC;
            sourceBuilder.sort(pamarVo.getSortbY(),order);
        }
        // 分页
        if(pamarVo.getPageSize() == null){
            sourceBuilder.from((int) ((pamarVo.getPage()-1)*Esconstant.DEFAULT_SIZE));
        }else {
            sourceBuilder.from((int) ((pamarVo.getPage()-1)*pamarVo.getPageSize()));
        }

        // TODO pageSize
        if(pamarVo.getPageSize() != null){
            sourceBuilder.size(Integer.parseInt(pamarVo.getPageSize().toString()));
        }else {
            sourceBuilder.size(Esconstant.DEFAULT_SIZE);
        }

        // 高亮
        if(!StringUtils.isEmpty(pamarVo.getKey())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("subTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }

        /**
         * 聚合
         */
        //5.1 按照品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        //品牌聚合的子聚合
        TermsAggregationBuilder brand_name_agg = AggregationBuilders.terms("brand_name_agg").field("brandName").size(1);
        TermsAggregationBuilder brand_img_agg = AggregationBuilders.terms("brand_img_agg").field("brandImg");
        brand_agg.subAggregation(brand_name_agg);
        brand_agg.subAggregation(brand_img_agg);
        sourceBuilder.aggregation(brand_agg);

        //5.2 按照catalog聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("cid3").size(20);
        TermsAggregationBuilder catalog_name_agg = AggregationBuilders.terms("catalog_name_agg").field("catelog3Name").size(1);
        catalog_agg.subAggregation(catalog_name_agg);
        sourceBuilder.aggregation(catalog_agg);

        //5.3 按照attrs聚合
        NestedAggregationBuilder nestedAggregationBuilder = new NestedAggregationBuilder("specs_agg", "specs");

        //按照attrId聚合
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("specsIdAgg").field("specs.specId");

        //按照attrId聚合之后再按照attrName和attrValue聚合
        TermsAggregationBuilder attr_name_agg = AggregationBuilders.terms("specsNameAgg").field("specs.specName").size(1);
        TermsAggregationBuilder attr_value_agg = AggregationBuilders.terms("specsValueAgg").field("specs.specValue").size(50);
        attr_id_agg.subAggregation(attr_name_agg);
        attr_id_agg.subAggregation(attr_value_agg);
        nestedAggregationBuilder.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(nestedAggregationBuilder);

        System.out.println("检索的dcl语句："+sourceBuilder.toString());
        return  new SearchRequest(new String[]{Esconstant.PRODUCT_INDEX},sourceBuilder);
    }


}
